package com.unswift.cloud.adapter.system.api;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;
import com.unswift.cloud.adapter.BaseAdapter;
import com.unswift.cloud.enums.system.api.ApiParamTypeEnum;
import com.unswift.cloud.enums.system.api.InterfacesParamCategoryEnum;
import com.unswift.cloud.mapper.system.api.param.SystemApiParamExtendMapper;
import com.unswift.cloud.mapper.system.api.param.SystemApiParamMapper;
import com.unswift.cloud.pojo.dao.sql.Sql;
import com.unswift.cloud.pojo.dao.system.api.interfaces.SystemApiInterfacesDeleteDo;
import com.unswift.cloud.pojo.dao.system.api.interfaces.SystemApiInterfacesInsertBatchItemDo;
import com.unswift.cloud.pojo.dao.system.api.param.SystemApiParamDataDo;
import com.unswift.cloud.pojo.dao.system.api.param.SystemApiParamInsertDo;
import com.unswift.cloud.pojo.dao.system.api.param.SystemApiParamSearchDo;
import com.unswift.cloud.pojo.dao.system.api.param.SystemApiParamSingleDo;
import com.unswift.cloud.pojo.vo.system.api.param.SystemApiParamTreeVo;
import com.unswift.utils.ClassUtils;
import com.unswift.utils.ExceptionUtils;
import com.unswift.utils.ObjectUtils;
import com.unswift.utils.StringUtils;

@Component
@Api(value="接口参数api公共服务-此bean可以被任何Service引用", author="unswift", date="2023-09-05", version="1.0.0")
public class SystemApiParamAdapter extends BaseAdapter{
	
	@Autowired
	@ApiField("接口参数api数据库操作对象")
	private SystemApiParamMapper systemApiParamMapper;
	
	@Autowired
	@ApiField("接口参数api数据库扩展操作对象")
	private SystemApiParamExtendMapper systemApiParamExtendMapper;
	
	@ApiMethod(value="获取接口参数api数据库操作对象", returns=@ApiField("数据库操作对象"))
	public SystemApiParamMapper getMapper(){
		return systemApiParamMapper;
	}
	
	@ApiMethod(value="获取接口参数api模块名称", returns=@ApiField("模块名称"))
	public String getModule(){
		return "systemApiParam";
	}
	
	@ApiMethod(value="根据主键查询单个对象", params=@ApiField("主键"), returns=@ApiField("主键匹配的对象"))
	public SystemApiParamDataDo findById(Long id){
		return this.findSingle(new SystemApiParamSingleDo(id, null));
	}
	
	@ApiMethod(value="根据主键列表查询列表对象", params=@ApiField("主键列表"), returns=@ApiField("主键列表匹配的列表对象"))
	public List<SystemApiParamDataDo> findByIds(List<Long> idList){
		return this.findList(new SystemApiParamSearchDo(idList));
	}
	
	@ApiMethod(value="根据查询对象查询首个对象", params={@ApiField("查询对象"), @ApiField("查询到多个是否抛出异常，可以传入指定提示语，如果提示语为空，则不提示，总的提示与格式：根据%s查询到多条数据，只需要传递%s部分即可")}, returns=@ApiField("首个对象"))
	public SystemApiParamDataDo findFirst(SystemApiParamSearchDo search, String multipleException){
		List<SystemApiParamDataDo> list=this.findList(search);
		if(ObjectUtils.isEmpty(list)){
			return null;
		}
		if(ObjectUtils.isNotEmpty(multipleException)){
			ExceptionUtils.trueException(list.size()>1, "multiple.data.found", multipleException);
		}
		return list.get(0);
	}
	
	@ApiMethod(value="接口对象的参数解析", params = {@ApiField("接口对象"), @ApiField("扫描的包列表")})
	public void handleInterfaces(List<SystemApiInterfacesInsertBatchItemDo> apiInterfacesList, List<String> scanPackages) {
		if(ObjectUtils.isEmpty(apiInterfacesList)) {
			return ;
		}
		int sort;
		ApiMethod methodAnno;
		Method method;
		ApiField[] paramComments;
		ApiField paramComment, returnComment;
		Parameter[] parameters;
		Class<?> returnType;
		SystemApiParamInsertDo insert;
		SystemApiInterfacesDeleteDo delete=new SystemApiInterfacesDeleteDo();
		Sql sql;
		for (SystemApiInterfacesInsertBatchItemDo apiInterfacesDo : apiInterfacesList) {
			sql=Sql.createSql();
			sql.addWhere(Sql.LOGIC_AND, "api_interface_id_", Sql.COMPARE_EQUAL, apiInterfacesDo.getId());
			delete.setSql(sql);
			this.deleteReal(delete);
			methodAnno=apiInterfacesDo.getMethodAnno();
			method=apiInterfacesDo.getMethodClass();
			paramComments=methodAnno.params();
            parameters = method.getParameters();
            sort=1;
            if(ObjectUtils.isNotEmpty(parameters)) {
            	int parametersLength = parameters.length;
                logger.error(method.getDeclaringClass().getName()+"."+method.getName());
                ExceptionUtils.empty(paramComments, "number.of.parameters.does.not.match", method.getDeclaringClass().getName()+"."+method.getName(), parametersLength, 0);
                int paramCommentLength = paramComments.length;
                logger.error("方法："+method.getDeclaringClass().getName()+"."+method.getName());
                ExceptionUtils.notEquals(parametersLength, paramCommentLength, "number.of.parameters.does.not.match", method.getDeclaringClass().getName()+"."+method.getName(), parametersLength, paramCommentLength);
                boolean isInsert;
                for (int i = 0; i < parameters.length; i++) {
                	paramComment=paramComments[i];
                	if(!paramComment.requestParam()) {//不是接口参数
                		continue;
                	}
                    insert=new SystemApiParamInsertDo();
                    insert.setApiInterfaceId(apiInterfacesDo.getId());
                    isInsert=true;
                    if(ObjectUtils.isNotEmpty(parameters[i].getAnnotation(PathVariable.class))) {
                    	insert.setParamCategory(InterfacesParamCategoryEnum.PATH.getKey());
                    	insert.setRequired(true);
                    }else if(ObjectUtils.isNotEmpty(parameters[i].getAnnotation(RequestParam.class))) {
                    	if(ObjectUtils.equals("GET", apiInterfacesDo.getMethod(), true)) {
                    		insert.setParamCategory(InterfacesParamCategoryEnum.URL.getKey());
                    	}else {
                    		insert.setParamCategory(InterfacesParamCategoryEnum.FORM.getKey());
                    	}
                    }else if(ObjectUtils.isNotEmpty(parameters[i].getAnnotation(RequestBody.class))) {
                    	insert.setParamCategory(InterfacesParamCategoryEnum.BODY.getKey());
                    	insert.setRequired(true);
                    }else {
                    	if(ObjectUtils.equals("GET", apiInterfacesDo.getMethod(), true)) {
                    		insert.setParamCategory(InterfacesParamCategoryEnum.URL.getKey());
                    	}else {
                    		insert.setParamCategory(InterfacesParamCategoryEnum.FORM.getKey());
                    	}
                    	if(StringUtils.startsWiths(parameters[i].getType().getName(), scanPackages)) {
                    		isInsert=false;
                    	}
                    }
                    insert.setParamType(ApiParamTypeEnum.match(parameters[i].getType())); 
                    if(StringUtils.startsWiths(parameters[i].getType().getName(), scanPackages)) {
                    	if(insert.getParamCategory().equals("body")){
                    		insert.setParamKey("array".equals(insert.getParamType())?"[":"{");
                    	}
                    }else {
                    	insert.setParamKey(parameters[i].getName());
                    }
                    insert.setParamComment(paramComment.value());
                    insert.setRule(paramComment.rule());
                    if(ObjectUtils.isEmpty(insert.getRequired())) {
                    	insert.setRequired(paramComment.required());
                    }
                    insert.setDeprecated(ObjectUtils.isNotEmpty(parameters[i].getAnnotation(Deprecated.class)));
                    insert.setTestExample(paramComment.testExample());
                    insert.setSort(sort);
                    if(isInsert) {
                    	this.save(insert, false);
                    }
                    this.handleChildParam(method.getGenericParameterTypes()[i], scanPackages, isInsert?insert:null, apiInterfacesDo, insert.getParamCategory());
                    sort++;
                }
            }
            returnComment=methodAnno.returns();
            returnType=method.getReturnType();
            insert=new SystemApiParamInsertDo();
            insert.setApiInterfaceId(apiInterfacesDo.getId());
            insert.setParamCategory(InterfacesParamCategoryEnum.RETURN.getKey());
            insert.setParamKey("{");
            insert.setParamComment(returnComment.value());
            insert.setParamType(ApiParamTypeEnum.match(returnType));                
            insert.setRule(returnComment.rule());
            insert.setRequired(returnComment.required());
            insert.setDeprecated(ObjectUtils.isNotEmpty(returnType.getAnnotation(Deprecated.class)));
            insert.setTestExample(returnComment.testExample());
            insert.setSort(sort);
            this.save(insert, false);
            this.handleChildParam(method.getGenericReturnType(), scanPackages, insert, apiInterfacesDo, null);
        }
	}
	
	@ApiMethod(value="处理子属性", params = {@ApiField("属性类型"), @ApiField("扫描的包"), @ApiField("父参数对象"), @ApiField("请求接口对象，父参数为空时使用"), @ApiField("参数分类，父参数为空时使用")})
	private void handleChildParam(Type type, List<String> scanPackages, SystemApiParamInsertDo parentDo, SystemApiInterfacesInsertBatchItemDo apiInterfacesDo, String paramCategory) {
		String name=type.getTypeName();
		Class<?> paramClass=null;
		Map<String, Type> paramMap=new HashMap<String, Type>();
		if(type instanceof ParameterizedType) {
			ParameterizedType paramType=(ParameterizedType)type;
			Type[] actualTypeArguments = paramType.getActualTypeArguments();
			if(parentDo.getParamType().equals(ApiParamTypeEnum.LIST.getValue())) {
				handleChildParam(actualTypeArguments[0], scanPackages, parentDo, apiInterfacesDo, paramCategory);
				return;
			}
			if(StringUtils.startsWiths(name, scanPackages)) {
				Type rawType = paramType.getRawType();
				if(rawType instanceof Class){
					paramClass=(Class<?>)rawType;
					TypeVariable<?>[] typeParameters = paramClass.getTypeParameters();
					for (int j = 0; j < typeParameters.length; j++) {
						paramMap.put(typeParameters[j].getTypeName(), actualTypeArguments[j]);
					}
				}
			}
			
		}else {
			if(StringUtils.startsWiths(name, scanPackages)) {
				paramClass=(Class<?>)type;
			}
		}
		if(ObjectUtils.isNotEmpty(paramClass)) {
			List<Field> fields=ClassUtils.getFieldList(paramClass);
			if(ObjectUtils.isNotEmpty(fields)) {
				ApiField fieldAnno;
				SystemApiParamInsertDo insert;
				int sort=1;
				for (Field field : fields) {
					fieldAnno=field.getAnnotation(ApiField.class);
					if(ObjectUtils.isEmpty(fieldAnno)) {
						continue;
					}
					insert=new SystemApiParamInsertDo();
	                insert.setApiInterfaceId(ObjectUtils.isNotEmpty(parentDo)?parentDo.getApiInterfaceId():apiInterfacesDo.getId());
                	insert.setParamCategory(ObjectUtils.isNotEmpty(parentDo)?parentDo.getParamCategory():paramCategory);
	                insert.setParamKey(field.getName());
	                insert.setParamComment(fieldAnno.value());
	                insert.setParamType(ApiParamTypeEnum.match(field.getType()));                
	                insert.setRule(fieldAnno.rule());
	                insert.setRequired(fieldAnno.required());
	                insert.setDeprecated(ObjectUtils.isNotEmpty(field.getAnnotation(Deprecated.class)));
	                insert.setSort(sort);
	                insert.setTestExample(fieldAnno.testExample());
	                insert.setParentId(ObjectUtils.isNotEmpty(parentDo)?parentDo.getId():null);
	                this.save(insert, false);
	                String typeName = field.getGenericType().getTypeName();
					if(paramMap.containsKey(typeName)) {
						handleChildParam(paramMap.get(typeName), scanPackages, insert, apiInterfacesDo, paramCategory);
					}else {
						handleChildParam(field.getGenericType(), scanPackages, insert, apiInterfacesDo, paramCategory);
					}
	                sort++;
				}
			}
		}
		
	}
	
	@ApiMethod(value="根据参数分类查询参数列表", params = {@ApiField("接口数据id"), @ApiField("参数分类")}, returns = @ApiField("参数列表"))
	public List<SystemApiParamDataDo> findByCategoryList(long apiInterfacesId, InterfacesParamCategoryEnum paramCategory){
		SystemApiParamSearchDo search=new SystemApiParamSearchDo();
		search.setApiInterfaceId(apiInterfacesId);
		search.setParamCategory(paramCategory.getKey());
		Sql sql=Sql.createSql();
		sql.addOrderBy("sort_", Sql.ORDER_BY_ASC);
		search.setSql(sql);
		return this.findList(search);
	}
	
	@ApiMethod(value="将参数列表转换为树结构", params = @ApiField("参数列表数据"), returns = @ApiField("参数树"))
	public List<SystemApiParamTreeVo> listToTree(List<SystemApiParamTreeVo> list){
		if(ObjectUtils.isNotEmpty(list)) {
			List<SystemApiParamTreeVo> tree=list.stream().filter(p -> ObjectUtils.isEmpty(p.getParentId())).collect(Collectors.toList());
			if(ObjectUtils.isNotEmpty(tree)) {
				for (SystemApiParamTreeVo param : tree) {
					loadChildren(list, param);
				}
			}
			return tree;
		}
		return null;
	}
	
	@ApiMethod(value="加载孩子参数", params = {@ApiField("参数列表"), @ApiField("父参数")})
	private void loadChildren(List<SystemApiParamTreeVo> list, SystemApiParamTreeVo parent) {
		if(ObjectUtils.isNotEmpty(list)) {
			List<SystemApiParamTreeVo> children=list.stream().filter(p -> parent.getId().equals(p.getParentId())).collect(Collectors.toList());
			if(ObjectUtils.isNotEmpty(children)) {
				parent.setChildren(children);
				for (SystemApiParamTreeVo child : children) {
					loadChildren(list, child);
				}
			}
		}
	}
}
